#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/vfs.h>
#include "daq_thread.h"
#include "socket_thread.h"
#include "rs485_thread.h"
#include "timet.h"
#include "afe_init.h"
#include "disk.h"
#include "atsha204.h"
#include "sha_thread.h"
#include "zynq_dna_uio.h"
#include "led_gpio_ctl.h"
#include "watchdog_thread.h"
#include "lm75a_thread.h"
#include "calibration.h"
#include "json_func.h"
#include "zlog.h"
#include "usr_typedef.h"
#include "rs485_init.h"
#include "json_parse.h"
#include "main.h"


/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/
sys_cfg_info		sys_cfg;								//系统配置相关信息
chx_cfg_info		chx_cfg[ACC_CH_TOTAL];					//加速度通道信息
spd_cfg_info		spd_cfg[SPD_CH_TOTAL];					//速度通道信息
rs485_cfg_info		rs485_cfg;								//RS485通道信息

sys_runtime			sys_rt;									//实时采样信息
acc_unit			acc_rt[ACC_CH_TOTAL];					//振动实时数据
spd_unit			spd_rt[SPD_CH_TOTAL];					//转速实时数据

//zlog_category_t *zc;
extern zynq_dna_handle		dna_dev;
extern afe_dev_info 		afe1_dev;


thread_status state_acc_daq, state_rs485_daq, state_socket;
thread_status state_sha_verify, state_watchdog, state_lm75a;


static int usr_zlog_init(void)
{
	int rc;

	//保存位置/mnt/eMMC/mmcblk0p1/log/
	rc = dzlog_init("/etc/zlog.conf", "*");
	if (rc) {
		printf("zlog init failed\n");
		return FAILURE;
	}
/*
	zc = zlog_get_category("*");
	if (!zc) {
		printf("zlog get category fail\n");
		zlog_fini();
		return -2;
	}
*/
	//dzlog是忽略分类(zlog_category_t)的一组简单zlog接口。它采用内置的一个默认
	//分类，这个分类置于锁的保护下。这些接口也是线程安全的。
	dzlog_info("\n################################################################");
    dzlog_info("zlog initialization succeeded!!!");

	return 0;
}


static void get_app_path(char *argv0, char *path)
{
	char *p, split[10] = "";
	uint32_t len;
	
	/*strrchr查找字符在指定字符串中从右面开始的第一次出现的位置，如果成功，
		返回该字符以及其后面的字符，如果失败，则返回 NULL*/
	p = strrchr(argv0, '/');
	len = strlen(argv0) - strlen(p);
	/*%m.ns m代表这个字符串一共输出m列，n代表取这个字符串前n个元素输出到整
		个m列的右侧，当n<m时，用空格补齐左边的余缺，当n>=m时，不做处理*/
	sprintf(split, "%%%d.%ds/", len, len);//split is %10.10s when len = 10
	sprintf(path, split, argv0);
}


static void system_default_config(sys_cfg_info *sys)
{
	sys->debug = ON;

	strcpy(sys->manufacturer, "ChengYuHangKang");				//制造商

	strcpy(sys->dev_name, "HK36B828A");
	strcpy(sys->dev_type, "Platform");
	strcpy(sys->dev_sn, "HK36B-23100101");
	strcpy(sys->dev_local, "BeiJing");
	strcpy(sys->fw_version, "v2.00.00");
	strcpy(sys->fw_date, "2023-10-15");

	sys->eigen_calc			= OFF;
	sys->trigger			= INTER;							//内部触发

	sys->samp_freq			= DEFAULT_DATA_SAMPLING_FREQ;		//实际采样率102400SPS
	sys->duration			= 1;								//持续时间1秒

	sys->impact_en 			= OFF;								//默认冲击不使能
	sys->impact_map			= 0;								//默认冲击解调映射转速0

	sys->order_en			= OFF;								//默认阶比不使能
	sys->order_map			= 0;								//默认阶比跟踪映射转速0
	sys->order_spd_thrd		= 240;								//默认阶比跟踪转速阈值
	
	sys->man_thd_slp 		= 3;
	sys->daq_thd_slp 		= 2;
	
	sys->socket_type		= TCP;
	sys->socket_port		= 6450;								//默认端口号
	
	sys->disk_free_size		= 24.0f * 1024;						//剩余空间24GB
	strcpy(sys->disk_path, "/mnt/eMMC/mmcblk0p2");				//会被后面json文件更新
	sys->disk_data_save		= OFF;
	sys->disk_data_save_inv	= 3600;	
	sys->disk_warn_thrd		= 500;								//硬盘空间预警门限（默认500MB）
	sys->disk_alarm_thrd	= 100;								//硬盘空间报警门限（默认100MB）
	sys->disk_chk_inv		= 60;								//磁盘检测周期	

	printf("system config variables init finished!\n");
}


static void speed_default_config(sys_cfg_info *sys, spd_cfg_info spdi[], spd_unit spdu[])
{	
	uint8_t ch;

	for(ch = 0; ch < SPD_CH_TOTAL; ch++) {
		spdu[ch].speed_pulse_raw = (uint8_t *)malloc(RAW_DATA_SIZE * MAX_DATA_DURATION * sizeof(uint32_t));			//2通道转速拼接成32bit数据
		spdu[ch].speed_val = 0.00;
		spdu[ch].speed_coeff = 1.00;
		spdi[ch].spd_en = ON;
		strcpy(spdi[ch].spd_desc, "spd-init");		
		spdi[ch].spd_pulse = 1.0f;															//默认周脉冲为1
		spdi[ch].spd_mode = PHOTO;
		spdi[ch].spd_calc = DIRECT;
		spdi[ch].spd_save_thrd = 600;
		strcpy(spdi[ch].spd_sensor.manufacturer, "ZhongHang");
		strcpy(spdi[ch].spd_sensor.modle, "ZA-CZ01");
		strcpy(spdi[ch].spd_sensor.sn, "881686");
		strcpy(spdi[ch].spd_sensor.type, "on-off");
		strcpy(spdi[ch].spd_sensor.unit, "rpm");
		strcpy(spdi[ch].spd_sensor.range, "1KHz");
	}
	
	if (sys->debug == ON)
		printf("speed channel variables init and memory allocation finished!\n");
}


static void chx_default_config(sys_cfg_info *sys, chx_cfg_info chx[])
{
	uint8_t ch;
	
	for (uint8_t i = 0; i < ACC_CH_TOTAL; i++)
	{
		chx[i].chx_en = OFF;
		strcpy(chx[i].chx_desc, "Vibra init");
		strcpy(chx[i].chx_sensor.manufacturer, "Endevco");
		strcpy(chx[i].chx_sensor.modle, "6222S-20A");
		strcpy(chx[i].chx_sensor.sn, "22536");
		strcpy(chx[i].chx_sensor.type, "ACC");
		strcpy(chx[i].chx_sensor.unit, "g");
		chx[i].chx_sample_freq = 102400;
		chx[i].chx_mode = SAFE;
		chx[i].chx_pga_gain = 1.0f;
		chx[i].chx_drcc_gain = 1.0f;
		chx[i].chx_fir_cutoff = LPFNO;
		chx[i].chx_sens = 1.0f;
		chx[i].chx_win_func = RECT;
		chx[i].chx_warning = 50.0f;
		chx[i].chx_alarm = 100.0f;
		chx[i].chx_plug_det = REMOVED;
		chx[i].chx_status = GOOD;
		chx[i].chx_offset = 0.0f;
		chx[i].chx_calib = 0.0f;
	}
	
	adc_cali_value_set(sys, chx);

	//内存申请
	for(ch = 0; ch < ACC_CH_TOTAL; ch++) {
		//等时采样内存申请
		acc_rt[ch].td_vdata_raw_volt = (float *)malloc(RAW_DATA_SIZE * MAX_DATA_DURATION * sizeof(float));		//原始电压值					
		acc_rt[ch].td_vdata_raw_g = (float *)malloc(RAW_DATA_SIZE * MAX_DATA_DURATION * sizeof(float));		//原始加速度信号
		//特征值清0
		for(uint8_t i = 0; i < (sizeof(td_calc_norl) / sizeof(float)); i++)
			*((float*)&(acc_rt[ch].td_eigen_value.td_mean) + i) = 0.0;
	}
	
	if (sys->debug == ON)
		printf("acceleration channel variables init and memory allocation finished!\n");

}


static void system_default_config_init(sys_cfg_info *sys, sys_runtime *rt, chx_cfg_info chx[], 
					spd_cfg_info spdi[], spd_unit spdu[], rs485_cfg_info *rs485, zynq_dna_handle *dna)
{
	rt->disk_chk_flag = 0;								//磁盘空间充足
	rt->json_cfg_file_recv = (char *)malloc(JSON_CONFIG_FILE_MAX_LEN * sizeof(char));
	rt->json_resv_file_recv = (char *)malloc(JSON_RESV_FILE_MAX_LEN * sizeof(char));

	zynq_dna_uuid_get(sys, dna);						//获取ZYNQ设备UUID

	system_default_config(sys);
	chx_default_config(sys, chx);
	speed_default_config(sys, spdi, spdu);
	rs485_default_config(sys, rs485);

	gpio_default_init(sys);

	//设置各进程状态挂起
	state_acc_daq.status 			= TS_SUSPEND;
	state_rs485_daq.status 			= TS_SUSPEND;
	state_socket.status 			= TS_SUSPEND;
	state_sha_verify.status 		= TS_SUSPEND;
	state_lm75a.status				= TS_SUSPEND;
	state_watchdog.status			= TS_SUSPEND;
}


static int thread_check(void)
{
	int chk = 0;

	chk =  (state_acc_daq.status 		!= TS_SUSPEND) ? 0 : 1;
	chk += (state_socket.status 		!= TS_SUSPEND) ? 0 : 1;
	chk += (state_lm75a.status 			!= TS_SUSPEND) ? 0 : 1;
	chk += (state_sha_verify.status 	!= TS_SUSPEND) ? 0 : 1;
	chk += (state_watchdog.status		!= TS_SUSPEND) ? 0 : 1;

	//如果有进程挂起，打印错误
	if(chk > 0){
		dzlog_error("not all the threads are runing!");
		printf("not all the threads are runing!\n");
	}
	
	return chk;
}


int main(int argc, char* argv[])
{
	double tmp1;

	/*初始化log系统*/
	usr_zlog_init();
	
	/*版权验证，验证失败程序直接退出*/
	if (sha256_copyright_verify() < 0) {
		dzlog_notice("#####the device is not authorized!!!#####");
		dzlog_notice("#######please contact the supplier#######");
		printf("#####the device is not authorized!!!#####\n");
		printf("#######please contact the supplier#######\n");
		exit(EXIT_FAILURE);
	}
	
	/*获取app路径,argv[0]为程序全名*/
	get_app_path(argv[0], sys_cfg.app_path);
	/*系统变量初始化*/
	system_default_config_init(&sys_cfg, &sys_rt, chx_cfg, spd_cfg, spd_rt, &rs485_cfg,  &dna_dev);
	/*根据json配置文件配置系统*/
	system_config_file_json_parse(&sys_cfg, chx_cfg, spd_cfg, &rs485_cfg);
	/*建立存储文件夹，失败则放弃存储*/
	creat_folder_of_save_file(&sys_cfg);
	/*模拟前端各通道默认模式加配置采样率*/
	afe_spi_default_config_init(&sys_cfg, &afe1_dev);

	dzlog_info("main: system initial is finished!");

	//创建多个子线程
	state_acc_daq.status = TS_READY;
	pthread_create(&state_acc_daq.tid, NULL, daq_acc_thread, &state_acc_daq);				//加速度数据采集线程

	state_socket.status = TS_READY;
	pthread_create(&state_socket.tid, NULL, socket_thread, &state_socket);					//TCP数据传输线程

	state_rs485_daq.status = TS_READY;
	pthread_create(&state_rs485_daq.tid, NULL, daq_rs485_thread, &state_rs485_daq);			//RS485 数据采集线程

	state_lm75a.status = TS_READY;
	pthread_create(&state_lm75a.tid, NULL, lm75a_temp_thread, &state_lm75a);				//环境温度监控线程

	state_watchdog.status = TS_READY;
	pthread_create(&state_watchdog.tid, NULL, watchdog_thread, &state_watchdog);			//看门狗线程

	state_sha_verify.status = TS_READY;
	pthread_create(&state_sha_verify.tid, NULL, sha_verify_thread, &state_sha_verify);		//版权验证

	sleep(3);
	if(thread_check() != 0)								//等待3秒如果有线程没有启动成功则退出应用程序
		exit(EXIT_FAILURE);

	dzlog_info("thread initial is finished start loop!");

    sys_rt.sys_chk_time = get_time_dbc();											//获取当前时间值

	while(1)
	{
		tmp1 = get_time_dbc();
		if( (tmp1 - sys_rt.sys_chk_time) > sys_cfg.disk_chk_inv) {  				//系统错误监测
			sys_rt.sys_chk_time = tmp1;	
			//afe_XXXX_check(sys_cfg, dat_raw);										//间隔检测
		}
		disk_size_check(&sys_cfg, &sys_rt);											//磁盘空间监测
		sleep(DISK_SIZE_CHECK_DELAY);
	}

	dzlog_info("all of thread  will stop to run!");

	//free动态内存
	state_acc_daq.status 		= TS_TERMINATED;
	state_socket.status 		= TS_TERMINATED;
	state_rs485_daq.status 		= TS_TERMINATED;
	state_lm75a.status 			= TS_TERMINATED;
	state_watchdog.status 		= TS_TERMINATED;
	state_sha_verify.status 	= TS_TERMINATED;

	pthread_cancel(state_socket.tid);					//强行结束tcp线程

	//pthread_join()即是子线程合入主线程，主线程阻塞等待子线程结束，然后回收子线程资源。
	//当函数返回时，被等待线程的资源被收回。如果线程已经结束，那么该函数会立即返回。

	pthread_join(state_acc_daq.tid, 	NULL);
	pthread_join(state_socket.tid, 		NULL);
	pthread_join(state_rs485_daq.tid, 	NULL);
	pthread_join(state_lm75a.tid, 		NULL);
	pthread_join(state_watchdog.tid, 	NULL);
	pthread_join(state_sha_verify.tid, 	NULL);


	state_acc_daq.status 		= TS_JOINED;
	state_socket.status 		= TS_JOINED;
	state_rs485_daq.status 		= TS_JOINED;
	state_lm75a.status 			= TS_JOINED;
	state_watchdog.status 		= TS_JOINED;
	state_sha_verify.status 	= TS_JOINED;
	
	return 0;
}
